# coding: utf8


import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.stats as stats
import os

plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False

class Data:
    data_dir = 'd:/seaborn-data'

    def __init__(self):
        self.dataset = dict()
        if os.path.isdir(Data.data_dir):
            files = [fname for fname in os.listdir(self.data_dir) if '.csv' in fname]
            names = [f.replace('.csv', '') for f in files]
            for name in names:
                self.get_dataset(name)
        else:
            raise NotADirectoryError

    def get_dataset(self, name):
        df = sns.load_dataset(name, data_home=Data.data_dir)
        self.dataset.update({name: df})


seaborn_dataset = Data().dataset
species = seaborn_dataset['iris']['species']


class DemoColor:

    @staticmethod
    def color_palette(exp=1):
        # get data
        tips = seaborn_dataset['tips']

        if exp == 1:
            # get palette name
            palettes = sns.palettes. SEABORN_PALETTES.keys()
            print(palettes)

        if exp == 2:
            # use palette, data to barplot
            fig, ax = plt.subplots(1, 3, sharey=True)
            for j, cpname in zip(range(3), ['colorblind', 'dark', 'muted']):
                cp = sns.color_palette(cpname)
                sns.barplot(x='size', y='tip', hue='day', data=tips, palette=cp, ax=ax[j])
                ax[j].set(title=cpname)

        if exp == 3:
            sns.palplot(sns.color_palette(), 0.6)

        if exp == 4:
            # set suffix _r or _d
            plt.figure(1)
            sns.palplot(sns.color_palette('Accent_r'), 0.6)
            plt.figure(2)
            sns.palplot(sns.color_palette('autumn_d'), 0.6)

        if exp == 5:
            print(sns.color_palette())
            with sns.color_palette('dark'):
                sns.relplot(x='size', y='tip', hue='day', data=tips)
                print(sns.color_palette())
            print(sns.color_palette())

        plt.show()


class Demoplot:

    # dataset = Data().dataset
    data_home = 'd:/seaborn-data'
    dataset = seaborn_dataset
    plt.rcParams['font.sans-serif'] = 'SimHei'
    plt.rcParams['axes.unicode_minus'] = False

    @staticmethod
    def barplot():
        sns.set()
        x = ['金融','农业','制造业','新能源']
        y = [164, 86, 126, 53]
        sns.barplot(x, y, order=['金融','制造业','农业','新能源'], saturation=0.2)

    @staticmethod
    def relplot(exp=1):
        sns.set_style('darkgrid')
        # tips = sns.load_dataset('tips', data_home=Demoplot.data_home)
        tips = Demoplot.dataset['tips']
        if exp == 1:
            g1 = sns.relplot(x="total_bill", y="tip", hue="time", style="sex", data=tips,
                             markers=['h', 'o'], dashes='-.', kind='scatter')
            g1.set(xlabel='total_bill(饭费)', ylabel='tip(小费)')
            plt.show()
        if exp == 2:
            g2 = sns.relplot(x="total_bill", y="tip", hue="day", col="time", data=tips)
            plt.show()
        if exp==3:
            g3 = sns.relplot(x="total_bill", y="tip", hue="day", col="time", row="sex", data=tips, kind='line')
            plt.show()
        if exp == 4:
            g4 = sns.relplot(x="total_bill", y="tip", hue="time", col="day", col_wrap=3, data=tips)
            plt.show()

    @staticmethod
    def distplot(exp=1):
        # dataset tips
        # tips = sns.load_dataset('tips', data_home=Demoplot.data_home)
        tips = Demoplot.dataset['tips']
        tips.index.name = 'tips_index'
        print(tips.info())

        # dataset norm x
        np.random.seed(0)
        x = np.random.randn(100)	# 生成100个来自标准正态分布的随机数

        if exp == 1:
            sns.set_style('darkgrid')
            ax = sns.distplot(x)
            plt.show()

        if exp == 2:
            import pandas as pd
            x = pd.Series(x, name="x variable")
            ax = sns.distplot(x)
            plt.show()

        if exp == 3:
            ax = sns.distplot(x, rug=True, hist=False)
            plt.show()

        if exp == 4:
            from scipy.stats import norm
            ax = sns.distplot(x, fit=norm, kde=False)
            plt.show()

        if exp == 5:
            ax = sns.distplot(x, vertical=True)
            plt.show()

        if exp == 6:
            # sns.set_color_codes()
            ax = sns.distplot(x, color="y")
            plt.show()

        if exp == 61:
            sns.set_color_codes()
            ax = sns.distplot(x, color="y")
            plt.show()

        if exp == 7:
            ax = sns.distplot(x, rug=True, rug_kws={"color": "g"},
                              kde_kws={"color": "k", "lw": 3, "label": "KDE"},
                              hist_kws={"histtype": "step", "linewidth": 3,
                                        "alpha": 1, "color": "g"}
                              )
            plt.show()

        if exp == 21:
            fig, axs = plt.subplots(1, 2)
            sns.distplot(tips['total_bill'], ax=axs[0])                     # 缺省为密度直方图
            sns.distplot(tips['total_bill'], kde=False, ax=axs[1])          # 绘制频数直方图
            plt.show()

    @staticmethod
    def jointplot(exp=1):
        tips = Demoplot.dataset['tips']
        iris = Demoplot.dataset['iris']
        if exp == 1:
            sns.set(style="white", color_codes=True)
            g = sns.jointplot(x="total_bill", y="tip", data=tips)
            plt.show()

        if exp == 2:
            g = sns.jointplot("total_bill", "tip", data=tips, kind="reg")
            plt.show()

        if exp == 3:
            g = sns.jointplot("total_bill", "tip", data=tips, kind="hex")
            plt.show()

        if exp == 4:
            g = sns.jointplot("sepal_width", "petal_length", data=iris,
                              kind="kde", space=0, color="g")
            plt.show()

        if exp == 5:
            g = (sns.jointplot("sepal_length", "sepal_width",
                               data=iris, color="k")
                 .plot_joint(sns.kdeplot, zorder=0, n_levels=6))
            plt.show()

        if exp == 6:
            x, y = np.random.randn(2, 300)
            g = (sns.jointplot(x, y, kind="hex")
                 .set_axis_labels("x", "y"))
            plt.show()

        if exp == 7:
            g = sns.jointplot("total_bill", "tip", data=tips,
                              height=5, ratio=3, color="g")
            plt.show()

        if exp == 8:
            g = sns.jointplot("petal_length", "sepal_length", data=iris,
                              marginal_kws=dict(bins=15, rug=True),
                              annot_kws=dict(stat="r"),
                              s=40, edgecolor="w", linewidth=1)
            plt.show()

    @staticmethod
    def freedman_diaconis(data, return_width=True):
        """
        Use Freedman Diaconis rule to compute optimal histogram bin width.
        ``returnas`` can be one of "width" or "bins", indicating whether
        the bin width or number of bins should be returned respectively.

        Parameters
        ----------
        data: np.ndarray
            One-dimensional array.

        return_width: {True, False}
            If True, return the estimated width for each histogram bin.
            If False, return the number of bins suggested by rule.
        """
        data = np.asarray(data, dtype=np.float_)
        IQR  = stats.iqr(data, rng=(25, 75), scale="raw", nan_policy="omit")
        N    = data.size
        bw   = (2 * IQR) / np.power(N, 1/3)

        if return_width:
            result = bw
        else:
            datmin, datmax = data.min(), data.max()
            datrng = datmax - datmin
            result = int((datrng / bw) + 1)

        return(result)

    @staticmethod
    def pairplot(exp=1):
        #导入iris数据集
        iris=sns.load_dataset("iris", data_home=Demoplot.data_home)
        # print(iris.head())

        if exp == 1:
            sns.set(style="ticks", color_codes=True)
            g = sns.pairplot(iris)
            plt.show()

        if exp == 2:
            g = sns.pairplot(iris, hue="species")
            plt.show()

        if exp == 3:
            g = sns.pairplot(iris, hue="species", palette="husl")
            plt.show()

        if exp == 4:
            g = sns.pairplot(iris, hue="species", markers=["o", "s", "D"])
            plt.show()

        if exp == 5:
            g = sns.pairplot(iris, vars=["sepal_width", "sepal_length"])
            plt.show()

        if exp == 6:
            g = sns.pairplot(iris, height=3,
                             vars=["sepal_width", "sepal_length"])
            plt.show()

        if exp == 7:
            g = sns.pairplot(iris,
                             x_vars=["sepal_width", "sepal_length"],
                             y_vars=["petal_width", "petal_length"])
            plt.show()

        if exp == 8:
            g = sns.pairplot(iris, diag_kind="kde")
            plt.show()

        if exp == 9:
            g = sns.pairplot(iris, kind="reg")
            plt.show()

        if exp == 10:
            g = sns.pairplot(iris, diag_kind="kde", markers="+",
                             plot_kws=dict(s=50, edgecolor="b", linewidth=1),
                             diag_kws=dict(shade=True))
            plt.show()

        if exp == 101:
            # 将列名换成中文名称
            iris.rename(columns={"sepal_length": "萼片长",
                                 "sepal_width": "萼片宽",
                                 "petal_length": "花瓣长",
                                 "petal_width": "花瓣宽",
                                 "species": "种类"},inplace=True)
            # 设置为中文种类名称
            kind_dict = {
                "setosa":"山鸢尾",
                "versicolor":"杂色鸢尾",
                "virginica":"维吉尼亚鸢尾"
            }
            iris["种类"] = iris["种类"].map(kind_dict)
            sns.pairplot(iris)
            plt.show()

    @staticmethod
    def test_units(kind='line'):
        #导入iris数据集
        data=sns.load_dataset("tips", data_home='d:/seaborn-data')

        data.rename(columns={'size': 'x', 'tip': 'y', 'sex': 'u', 'smoker': 's'}, inplace=True)
        data = data.astype(dtype={'u': str})
        # print(data.head())
        # print(data.info())

        if kind == 'cat':
            # for catplot
            # fig1, ax1 = plt.subplots()
            sns.catplot(x='x', y='y', data=data, ci=50, kind='swarm', s=10, estimator=None,
                        )
            # fig2, ax2 = plt.subplots()
            sns.catplot(x='x', y='y', data=data, units='u', ci=50, kind='swarm', s=10, estimator=None,
                        )

        if kind == 'line':
            # for lineplot
            fig, (ax1, ax2) = plt.subplots(1, 2)
            sns.lineplot(x='x', y='y', data=data, estimator=None, linewidth=5, ax=ax1)
            sns.lineplot(x='x', y='y', data=data, units='u', estimator=None, linewidth=5, ax=ax2)

        if kind == 'lm':
            # for lmplot
            data =pd.DataFrame({
                'x': [1, 1, 1, 1, 3, 3, 3, 4, 4, 4],
                'y': [1, 10, 3, 8, 5, 4, 17, 20, 5, 22],
                'u': [1, 2, 1, 2, 1, 1, 2, 2, 1, 2],
                'h': ['1', '2', '1', '1', '1', '1', '2', '2', '1', '2']
            })
            plt.figure(1)
            sns.lmplot(x='x', y='y', data=data)
            plt.figure(2)
            sns.lmplot(x='x', y='y', data=data, units='u')

        if kind == 'cat2':
            # cat2
            genes = pd.Series(["Gene1"] * 16 + ["Gene2"] * 16)
            conditions = pd.Series(np.tile(np.array(["Condition1"] * 8 + ["Condition2"] * 8), 2))
            wellID = pd.Series(np.array(
                ["W1"] * 4 + ["W2"] * 4 + ["W3"] * 4 + ["W4"] * 4 + ["W5"] * 4 + ["W6"] * 4 + ["W7"] * 4 + ["W8"] * 4))
            fluo = pd.Series(np.array([np.sort(np.random.logistic(size=4)) for _ in range(8)]).flatten())
            cycles = pd.Series(np.tile(np.array([0, 1, 2, 3]), 8))
            df = pd.concat([genes, conditions, wellID, cycles, fluo], axis=1)
            df.columns = ["Gene", "Condition", "WellID", "Cycle", "Fluo"]
            # print(df.info())
            print(df)

            kind = 'strip'
            # plt.figure(1)
            # sns.catplot(x="Cycle", y="Fluo", hue="Condition", estimator=None,
            #             data=df.loc[df.Gene == "Gene1"], kind=kind)
            # plt.figure(2)
            # sns.catplot(x="Cycle", y="Fluo", hue="Condition", units="WellID", estimator=None,
            #             data=df.loc[df.Gene == "Gene1"], kind=kind)
            fig, ax1 = plt.subplots()
            sns.lineplot(x="Cycle", y="Fluo", hue="Condition", estimator=None,
                         data=df.loc[df.Gene == "Gene1"], ax=ax1)
            fig, ax2 = plt.subplots()
            sns.lineplot(x="Cycle", y="Fluo", hue="Condition", units="WellID", estimator=None,
                         data=df.loc[df.Gene == "Gene1"], ax=ax2)

        plt.show()

    @staticmethod
    def lineplot():
        genes = pd.Series(["Gene1"] * 16 + ["Gene2"] * 16)
        conditions = pd.Series(np.tile(np.array(["Condition1"] * 8 + ["Condition2"] * 8), 2))
        wellID = pd.Series(np.array(
            ["W1"] * 4 + ["W2"] * 4 + ["W3"] * 4 + ["W4"] * 4 + ["W5"] * 4 + ["W6"] * 4 + ["W7"] * 4 + ["W8"] * 4))
        fluo = pd.Series(np.array([np.sort(np.random.logistic(size=4)) for _ in range(8)]).flatten())
        cycles = pd.Series(np.tile(np.array([0, 1, 2, 3]), 8))
        df = pd.concat([genes, conditions, wellID, cycles, fluo], axis=1)
        df.columns = ["Gene", "Condition", "WellID", "Cycle", "Fluo"]
        print(df)
        plt.figure()
        sns.lineplot(x="Cycle", y="Fluo", hue="Condition", units="WellID", estimator=None,
                     data=df.loc[df.Gene == "Gene1"])
        plt.figure()
        sns.lineplot(x="Cycle", y="Fluo", hue="Condition", estimator=None,
                     data=df.loc[df.Gene == "Gene1"])
        plt.show()

    @staticmethod
    def catplot(exp=1):
        exercise = seaborn_dataset['exercise'].drop('Unnamed: 0', axis=1)
        titanic = seaborn_dataset["titanic"]
        tips = seaborn_dataset['tips']

        if exp == 1:
            sns.set(style="ticks")
            exercise = sns.load_dataset("exercise")
            g = sns.catplot(x="time", y="pulse", hue="kind", data=exercise)

        if exp == 2:
            g = sns.catplot(x="time", y="pulse", hue="kind",
                            data=exercise, kind="violin")

        if exp == 3:
            g = sns.catplot(x="time", y="pulse", hue="kind",
                            col="diet", data=exercise)

        if exp == 4:
            g = sns.catplot(x="time", y="pulse", hue="kind",
                            col="diet", data=exercise,
                            height=5, aspect=.8)

        if exp == 5:
            g = sns.catplot("alive", col="deck", col_wrap=4,
                            data=titanic[titanic.deck.notnull()],
                            kind="count", height=2.5, aspect=.8)

        if exp == 6:
            g = sns.catplot(x="age", y="embark_town",
                            hue="sex", row="class",
                            data=titanic[titanic.embark_town.notnull()],
                            orient="h", height=2, aspect=3, palette="Set3",
                            kind="violin", dodge=True, cut=0, bw=.2)

        if exp == 7:
            g = sns.catplot(x="who", y="survived", col="class",
                            data=titanic, saturation=.5,
                            kind="bar", ci=None, aspect=.6)
            (g.set_axis_labels("", "Survival Rate")
                .set_xticklabels(["Men", "Women", "Children"])
                .set_titles("{col_name} {col_var}")
                .set(ylim=(0, 1))
                .despine(left=True))

        if exp == 81:
            # error graph
            sns.catplot(x='day', y='tip', data=tips, kind='bar')

        if exp == 82:
            # error graph
            # use ci percent as error bar
            sns.catplot(x='day', y='tip', data=tips, kind='bar', ci=50)

        if exp == 83:
            # error graph
            # use std as error bar
            sns.catplot(x='day', y='tip', data=tips, kind='bar', ci='sd')

        plt.show()


    @staticmethod
    def lmplot(exp=1):
        tips = Demoplot.dataset['tips']
        iris = seaborn_dataset["iris"]

        if exp == 1:
            sns.regplot('total_bill', 'tip', data=tips)  # , x_partial='total_bill')
            plt.show()

        if exp == 2:
            g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips)
            plt.show()

        if exp == 3:
            g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
                           markers = ["o", "x"])
            plt.show()

        if exp == 4:
            g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
                           palette = "Set1")
            plt.show()

        if exp == 5:
            g = sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
                           palette = dict(Yes="g", No="m"))
            plt.show()

        if exp == 6:
            g = sns.lmplot(x="total_bill", y="tip", col="smoker", data=tips)
            plt.show()

        if exp == 7:
            g = sns.lmplot(x="size", y="total_bill", hue="day", col="day",
                           data = tips, height = 6, aspect = .4, x_jitter = .1)
            plt.show()

        if exp == 8:
            g = sns.lmplot(x="total_bill", y="tip", col="day", hue="day",
                           data = tips, col_wrap = 2, height = 3)
            plt.show()

        if exp == 9:
            g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time",
                           data=tips, height=3)
            pass
            plt.show()

        if exp == 10:
            g = sns.lmplot(x="total_bill", y="tip", row="sex", col="time",
                           data = tips, height = 3)
            g = (g.set_axis_labels("Total bill (US Dollars)", "Tip")
                      .set(xlim=(0, 60), ylim=(0, 12),
                           xticks=[10, 30, 50], yticks=[2, 6, 10])
                      .fig.subplots_adjust(wspace=.02))
            plt.show()

        if exp == 101:
            # syntax x_partial
            # code from: https://github.com/mwaskom/seaborn/issues/458
            # the result show: the augment may can not be used!
            sns.regplot("sepal_length", "sepal_width", data=iris,
                        x_partial=iris[["petal_length", "petal_width"]])
            plt.show()

        if exp == 102:
            # syntax units
            data =pd.DataFrame({
                'x': [1, 1, 3, 3, 5, 5, 2, 2],
                'y': [3, 3, 1, 1, 2, 2, 5, 5],
                'u': [10, 10, 30, 30, 50, 60, 70, 90]
            })
            sns.lmplot(x='x', y='y', data=data, scatter_kws=dict(s=100))
            sns.lmplot(x='x', y='y', data=data, units='u', scatter_kws=dict(s=100))
            # sns.catplot(x='size', y='tip', hue='sex', data=data)    #, units='day')
            plt.show()

    @staticmethod
    def heatmap(exp=1):
        flights = sns.load_dataset("flights")
        flights = flights.pivot("month", "year", "passengers")
        np.random.seed(0)
        uniform_data = np.random.rand(10, 12)

        if exp == 1:
            ax = sns.heatmap(uniform_data)
            plt.show()

        if exp == 2:
            ax = sns.heatmap(uniform_data, vmin=0, vmax=1)
            plt.show()

        if exp == 3:
            normal_data = np.random.randn(10, 12)		# 生成二维正态分布矩阵数据
            ax = sns.heatmap(normal_data, center=0)
            plt.show()

        if exp == 4:
            ax = sns.heatmap(flights)
            plt.show()

        if exp == 5:
            ax = sns.heatmap(flights, annot=True, fmt="d")
            plt.show()

        if exp == 6:
            ax = sns.heatmap(flights, linewidths=.5)
            plt.show()

        if exp == 7:
            ax = sns.heatmap(flights, cmap="YlGnBu")
            plt.show()

        if exp == 8:
            ax = sns.heatmap(flights, center=flights.loc["January", 1955])
            plt.show()

        if exp == 9:
            data = np.random.randn(50, 20)
            ax = sns.heatmap(data, xticklabels=2, yticklabels=False)
            plt.show()

        if exp == 10:
            ax = sns.heatmap(flights, cbar=False)
            plt.show()

        if exp == 11:
            grid_kws = {"height_ratios": (.9, .05), "hspace": .3}
            f, (ax, cbar_ax) = plt.subplots(2, gridspec_kw=grid_kws)
            ax = sns.heatmap(flights, ax=ax,
                             cbar_ax=cbar_ax,
                             cbar_kws={"orientation": "horizontal"})
            plt.show()

        if exp == 12:
            corr = np.corrcoef(np.random.randn(10, 200))    # 生成相关系数数组corr，维度为10x10
            mask = np.zeros_like(corr)                      # 创建与corr维度相同的全0数组
            mask[np.triu_indices_from(mask)] = True         # 设置上三角元素为True
            with sns.axes_style("white"):
                 ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True)
            plt.show()

    @staticmethod
    def clustermap(exp=1):
        iris = seaborn_dataset["iris"]
        if 'species' in iris.columns:
            iris.pop("species")

        if exp == 1:
            sns.set(color_codes=True)
            g = sns.clustermap(iris)
            plt.show()

        if exp == 2:
            # use different metric
            g = sns.clustermap(iris, metric="correlation")
            plt.show()

        if exp == 3:
            # use different cluster method
            g = sns.clustermap(iris, method="single")
            plt.show()

        if exp == 4:
            # Use a different colormap and ignore outliers in colormap limits
            g = sns.clustermap(iris, cmap="mako", robust=True)
            plt.show()

        if exp == 5:
            # change fig size
            g = sns.clustermap(iris, figsize=(6, 7))
            plt.show()

        if exp == 6:
            # Plot one of the axes in its original organization:
            g = sns.clustermap(iris, col_cluster=False)
            plt.show()

        if exp == 7:
            # add color labels
            lut = dict(zip(species.unique(), "rbg"))
            row_colors = species.map(lut)
            g = sns.clustermap(iris, row_colors=row_colors)
            plt.show()

        if exp == 8:
            # standard data
            g = sns.clustermap(iris, standard_scale=1)
            plt.show()

        if exp == 9:
            # normalize data
            g = sns.clustermap(iris, z_score=0)
            plt.show()

        if exp == 10:
            plt.show()
